/*************************************************************
 * * Copyright (c) 2008-2011  Hillstonenet
 *
 * All Rights Reserved
 * 
 * This is the proprietary source code of Hillstonenet Company product HSM FORNAX
 *
 * @author zrmao
 *
 * 2011-7-15 16:08:33
 * 
 ***************************************************************/
package com.hillstone.hsm.swtool.shellp.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.log4j.Logger;

import com.hillstone.hsm.swtool.shellp.constant.Constant;

public class CommandUtil {
	private static final String PLAT_WINDOWS = "windows";
	private static final String PLAT_UNIX = "unix";
	private final static Logger logger = Logger.getLogger(CommandUtil.class);

	public final static String ERROR = "error";

	public final static String SUCCESS = "success";

	/**
	 * 执行内部命令
	 *
	 * @param cmd
	 * @throws Exception
	 */
	public static List<String> executeCmdString(String cmd) throws Exception {
		logger.info("execute command:" + cmd);
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, cmd);

		List<String> rtnList = new ArrayList<String>();

		Process p = null;
		BufferedReader inBr = null;
		BufferedInputStream in = null;
		try {
			Runtime run = Runtime.getRuntime();
			p = run.exec(cmds);
			in = new BufferedInputStream(p.getInputStream());
			inBr = new BufferedReader(new InputStreamReader(in));
			String lineStr;
			p.waitFor();
			while ((lineStr = inBr.readLine()) != null) {
				rtnList.add(lineStr);
				logger.debug("BufferedReader  line:" + lineStr);
			}

			if (p.waitFor() != 0) {
				if (p.exitValue() == 1)
					logger.error(cmd + "命令执行失败!");

			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.toString());
		}

		return rtnList;
	}

	public static List<String> executeCmdStrTime1(String cmd) throws Exception {
		logger.info("execute command:.............." + cmd);
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, cmd);

		List<String> rtnList = new ArrayList<String>();
		{
			Process p = null;
			BufferedReader inBr = null;
			BufferedInputStream err = null;
			BufferedReader errBr = null;
			BufferedInputStream in = null;
			try {
				Runtime run = Runtime.getRuntime();
				p = run.exec(cmds); // 启动另一个进程来执行命令
				in = new BufferedInputStream(p.getInputStream());
				err = new BufferedInputStream(p.getErrorStream());
				inBr = new BufferedReader(new InputStreamReader(in));
				errBr = new BufferedReader(new InputStreamReader(err));
				String lineStr;
				p.waitFor();
				while ((lineStr = inBr.readLine()) != null) {
					// System.out.println("----------------" + lineStr);
					logger.info("execure-result:" + lineStr);
					rtnList.add(lineStr);

				}

				p.notifyAll();

				// 检查命令是否执行失败。
				try {
					if (p.waitFor() != 0) {
						if (p.exitValue() == 1) // p.exitValue()==0表示正常结束，1：非正常结束
							logger.error(cmd + " 命令执行失败!");

					}
				} catch (Exception e) {
					// e.printStackTrace();
					logger.error(e.toString());
				}

			} catch (Exception e) {
				e.printStackTrace();
				logger.error(e.toString());
			} finally {
				if (errBr != null)
					errBr.close();
				if (err != null)
					err.close();
				if (inBr != null)
					inBr.close();
				if (in != null)
					in.close();
				if (p != null)
					p.destroy();
			}
		}
		return rtnList;
	}

	public static List<String> executeCmdStr(String cmd) {
		logger.info("execute command:" + cmd);
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, cmd);

		List<String> rtnList = new ArrayList<String>();
		{

			try {
				logger.warn(Arrays.toString(cmds));
				Runtime run = Runtime.getRuntime();
				final Process p = run.exec(cmds); // 启动另一个进程来执行命令

				new Thread(new Runnable() {

					@Override
					public void run() {
						try {
							readInputStream(p.getErrorStream());
						} catch (IOException e) {
							logger.error(e, e);
						}

					}
				}).start();

				rtnList = readInputStream(p.getInputStream());

				/*
				 * int c = p.waitFor(); if (c != 0) { System.out.println("处理失败：" + cmd); }
				 */
			} catch (Exception e) {
				// e.printStackTrace();
				logger.error(e.toString());
			} finally {
				// p.destroy();
			}
		}
		return rtnList;
	}

	private static List<String> readInputStream(InputStream is) throws IOException {
		List<String> rtnList = new ArrayList<String>();
		BufferedReader br = new BufferedReader(new InputStreamReader(is));

		try {
			for (String line = br.readLine(); line != null; line = br.readLine()) {
				// System.out.println("execute:" + line);
				rtnList.add(line);
				// lines.append(line);
			}
		} catch (Exception e) {
			logger.error("readInputStream is error." + e.toString());
		} finally {
			br.close();
			is.close();
		}
		return rtnList;
	}

	public static List<String> executeCmdStr(String cmd, String[] env) throws Exception {
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, cmd);

		List<String> rtnList = new ArrayList<String>();
		{
			Process p = null;
			BufferedReader inBr = null;
			BufferedInputStream err = null;
			BufferedReader errBr = null;
			BufferedInputStream in = null;
			try {
				Runtime run = Runtime.getRuntime();
				p = run.exec(cmds, env); // 启动另一个进程来执行命令
				in = new BufferedInputStream(p.getInputStream());
				err = new BufferedInputStream(p.getErrorStream());
				inBr = new BufferedReader(new InputStreamReader(in));
				errBr = new BufferedReader(new InputStreamReader(err));
				String lineStr;
				p.waitFor();
				while ((lineStr = inBr.readLine()) != null) {
					// System.out.println(lineStr);
					rtnList.add(lineStr);
				}

				// 检查命令是否执行失败。
				try {
					if (p.waitFor() != 0) {
						if (p.exitValue() == 1) // p.exitValue()==0表示正常结束，1：非正常结束
							logger.error(cmd + " 命令执行失败!");

					}
				} catch (Exception e) {
					// e.printStackTrace();
					logger.error(e.toString());
				}

			} catch (Exception e) {
				// e.printStackTrace();
				logger.error(e.toString());
			} finally {
				if (errBr != null)
					errBr.close();
				if (err != null)
					err.close();
				if (inBr != null)
					inBr.close();
				if (in != null)
					in.close();
				if (p != null)
					p.destroy();
			}
		}
		return rtnList;
	}

	// 执行命令开始
	public static void executeCmd(String shellCommand) throws IOException {
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, shellCommand);
		Runtime rt = Runtime.getRuntime();
		rt.exec(cmds);
		shellCommand = null;
		rt = null;
	}

	// 执行命令开始
	public static Process executeCmdRtProcess(String shellCommand) throws IOException {
		String platform = getPlatform();
		String[] cmds = getExecuteCmd(platform, shellCommand);
		Runtime rt = Runtime.getRuntime();
		Process process = rt.exec(cmds);
		shellCommand = null;
		rt = null;
		return process;
	}

	public static String getPlatform() {
		Properties props = System.getProperties();
		String name = props.getProperty("os.name");
		if (name.toLowerCase().startsWith("win")) {
			return PLAT_WINDOWS;
		} else {
			return PLAT_UNIX;
		}
	}

	public static String[] getExecuteCmd(String platform, String cmd) {
		if (platform.equals(PLAT_WINDOWS)) {
			return new String[] { "C:/Windows/System32/cmd.exe", "/c", cmd };
		}
		return new String[] { "/bin/sh", "-c", cmd };
	}

	////////////////////////////////////////////////////////////
	// add by xfengli //
	// it's recommended to use this method //
	////////////////////////////////////////////////////////////
	public static Map<String, List<String>> goodExecutorCmd(String... cmds) {
		Runtime runtime = Runtime.getRuntime();
		Map<String, List<String>> map = new HashMap<String, List<String>>();
		final List<String> errInfoList = new ArrayList<String>();

		InputStream stdErrorStream = null;
		BufferedReader errBufferedReader = null;
		Process process = null;
		logger.warn("execute command: " + Arrays.asList(cmds));

		try {
			// runtime.exec has overloaded implementation, to call the right one
			if (cmds.length > 1) {
				process = runtime.exec(cmds);
			} else {
				process = runtime.exec(cmds[0]);
			}

			Future<List<String>> okResult = getOkResult(process);

			stdErrorStream = process.getErrorStream();
			errBufferedReader = new BufferedReader(new InputStreamReader(stdErrorStream));
			String errorStr = null;

			while ((errorStr = errBufferedReader.readLine()) != null) {
				if (StringUtil.isNotEmpty(errorStr)) {
					logger.info("error result: " + errorStr);
					errInfoList.add(errorStr);
				}
			}

			if (okResult != null) {
				map.put(Constant.SUCCESS, okResult.get());
			}

			map.put(Constant.ERROR, errInfoList);
		} catch (Exception e) {
			// e.printStackTrace();
			errInfoList.add(e.getMessage());
			map.put(Constant.ERROR, errInfoList);

			logger.error("execute cmd err! ", e);
		}

		finally {
			if (stdErrorStream != null) {
				try {
					stdErrorStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					// e.printStackTrace();
				}
			}
			if (errBufferedReader != null) {
				try {
					errBufferedReader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					// e.printStackTrace();
				}
			}
			if (process != null) {
				process.destroy();
			}
		}

		return map;
	}

	/**
	 * Add by liufei Date 2017-12-7 在执行命令时，根据platform组合命令，否则类似于管道操作、sed命令将报错
	 * 
	 * @param cmdList
	 * @return
	 */
	public static Map<String, List<String>> executeCmdListWithPlatform(List<String> cmdList) {
		Map<String, List<String>> resultMap = new HashMap<String, List<String>>();
		resultMap.put(SUCCESS, new ArrayList<String>());
		for (String cmd : cmdList) {
			String platform = getPlatform();
			String[] cmds = getExecuteCmd(platform, cmd);
			Map<String, List<String>> result = goodExecutorCmd(cmds);
			if (CollectionUtil.isNotEmpty(result.get(ERROR))) {
				result.get(ERROR).add(0, cmd);
				resultMap.put(ERROR, result.get(ERROR));
				return resultMap;
			} else {
				resultMap.get(SUCCESS).addAll(result.get(SUCCESS));
			}
		}

		return resultMap;
	}

	private static Future<List<String>> getOkResult(final Process process) {
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Future<List<String>> result = executor.submit(new Callable<List<String>>() {

			public List<String> call() {
				// TODO Auto-generated method stub
				InputStream stdOkStream = null;
				BufferedReader okBufferedReader = null;
				List<String> okInfoList = new ArrayList<String>();
				try {
					stdOkStream = process.getInputStream();
					okBufferedReader = new BufferedReader(new InputStreamReader(stdOkStream));
					String br = null;
					while ((br = okBufferedReader.readLine()) != null) {
						okInfoList.add(br);
					}
				} catch (Exception e) {
					// e.printStackTrace();
				}

				finally {
					if (stdOkStream != null) {
						try {
							stdOkStream.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							// e.printStackTrace();
						}
					}
					if (okBufferedReader != null) {
						try {
							okBufferedReader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							// e.printStackTrace();
						}
					}
				}
				return okInfoList;
			}

		});

		return result;
	}

}
